﻿// -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	SPSiteExts.cs
//  Developer:	Igor Solomatov
//  Created:	12.11.2012
// 
// -------------------- iSharePoint -------------------- //

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace Smart.Common
{
	[DebuggerStepThrough]
	public static class SPSiteExts
	{
		/// <summary>
		/// Opens web by <paramref name="webId"/> from <paramref name="site"/>, checks for root web
		/// </summary>
		public static ManagedDisposable<SPWeb> GetWeb(this SPSite site, Guid webId)
		{
			try
			{
				var rootWeb = site.RootWeb;

				if (rootWeb.ID == webId)
					return new ManagedDisposable<SPWeb>(rootWeb, false);

				var disposable = new ManagedDisposable<SPWeb>(site.OpenWeb(webId), true);

				if (disposable.Disposable != null && !disposable.Disposable.Exists)
				{
					disposable.Dispose();
					return null;
				}

				return disposable;
			}
			catch (Exception x)
			{
				H.LX("SPSiteExts.GetWeb", "GetWeb webId: " + webId, x);
			}

			return null;
		}

		/// <summary>
		/// Opens web by <paramref name="webUrl"/> from <paramref name="site"/>, checks for root web.
		/// If <paramref name="exactUrl"/> specified - check for exact web url
		/// </summary>
		public static ManagedDisposable<SPWeb> GetWeb(this SPSite site, string webUrl, bool exactUrl)
		{
			try
			{
				var rootWeb = site.RootWeb;

				if (rootWeb.ServerRelativeUrl.EqIC(webUrl) || rootWeb.Url.EqIC(webUrl))
					return new ManagedDisposable<SPWeb>(rootWeb, false);

				var disposable = new ManagedDisposable<SPWeb>(site.OpenWeb(webUrl, exactUrl), true);

				if (disposable.Disposable != null && !disposable.Disposable.Exists)
				{
					disposable.Dispose();
					return null;
				}

				return disposable;
			}
			catch (Exception x)
			{
				H.LX("SPSiteExts.GetWeb", "GetWeb webUrl: " + webUrl, x);
			}

			return null;
		}

		/// <summary>
		/// Searches web by <paramref name="webUrl"/> from <paramref name="site"/>, checks for root web.
		/// </summary>
		public static ManagedDisposable<SPWeb> FindWebByUrl(this SPSite site, string webUrl)
		{
			return site.GetWeb(webUrl, false);
		}

		/// <summary>
		/// Run <paramref name="code"/> under elevated mode for <paramref name="site"/>
		/// </summary>
		/// <exception cref="TargetInvocationException">Exception rethrowed</exception>
		public static bool RunElevated(this SPSite site, SPSecurity.CodeToRunElevated code)
		{
			var res = true;

			if (SPH.UnderElevatedMode())
			{
				// Reuse elevated mode, instead of entering it twice

				H.LT("RunElevated: Reusing elevated mode");

				SPH.EnterElevatedMode(site);

				try
				{
					code();
				}
				catch (Exception x)
				{
					//res = false;
					H.LX("RunElevated.r", x);
					throw new TargetInvocationException(x);
				}
				finally
				{
					SPH.LeaveElevatedMode();
				}
			}
			else
			{
				H.LT("RunElevated: Using new elevated mode");

				SPH.ValidateFormDigest();

				SPSecurity.RunWithElevatedPrivileges(
					delegate
					{
						SPH.EnterElevatedMode(site);

						try
						{
							code();
						}
						catch (Exception x)
						{
							res = false;
							H.LX("RunElevated.n", x);
							throw new TargetInvocationException(x);
						}
						finally
						{
							SPH.LeaveElevatedMode();
						}
					});
			}

			return res;
		}

		/// <summary>
		/// Executes <paramref name="method"/> for each web in <paramref name="site"/>
		/// </summary>
		/// <exception cref="ArgumentNullException"><paramref name="method"/> is null</exception>
		public static void ForEachWeb(this SPSite site, Action<SPWeb> method)
		{
			if (site == null) return;
			if (method == null) throw new ArgumentNullException("method");

			var allWebs = site.AllWebs;

			for (int webIdx = 0, webIdxMax = allWebs.Count; webIdx < webIdxMax; webIdx++)
				using (var web = allWebs[webIdx])
					method(web);
		}

		/// <summary>
		/// Executes <paramref name="method"/> for each web in <paramref name="site"/>, all webs are saved in temporary collection
		/// </summary>
		/// <exception cref="ArgumentNullException"><paramref name="method"/> is null</exception>
		public static void SafeForEachWeb(this SPSite site, Action<SPWeb> method)
		{
			if (site == null) return;
			if (method == null) throw new ArgumentNullException("method");

			var allWebs = site.AllWebs.ToArray();

			for (int webIdx = 0, webIdxMax = allWebs.Length; webIdx < webIdxMax; webIdx++)
				using (var web = allWebs[webIdx])
					method(web);
		}

		public static void RunWithList(this SPSite site, string baseUrl, string listBaseUrlRelative, Action<SPSite, SPWeb, SPList> method)
		{
			ManagedDisposable<SPWeb> dWeb;
			SPList list;

			site.OpenList(baseUrl, listBaseUrlRelative, out dWeb, out list);

			using (dWeb)
				method(site, dWeb.Disposable, list);
		}

		public static void OpenList(this SPSite site, string baseUrl, string listBaseUrlRelative, out ManagedDisposable<SPWeb> lWeb, out SPList list)
		{
			var fullUrl = SPUrlUtility.CombineUrl(baseUrl, listBaseUrlRelative);

			lWeb = site.FindWebByUrl(fullUrl);

			list = lWeb.Disposable.GetListByUrl(fullUrl);
		}


		/// <summary>
		/// Read whole file
		/// </summary>
		/// <exception cref="ArgumentNullException"><paramref name="site"/> is null</exception>
		/// <exception cref="ArgumentNullException"><paramref name="fileUrl"/> is null</exception>
		public static byte[] ReadFile(this SPSite site, string fileUrl)
		{
			if (site == null) throw new ArgumentNullException("site");
			if (fileUrl == null) throw new ArgumentNullException("fileUrl");

			using (var dWeb = site.FindWebByUrl(fileUrl))
			{
				var web = dWeb.Disposable;

				return web.ReadFile(web.GetWebRelativeUrl(fileUrl));
			}
		}

		/// <summary>
		/// Read whole file
		/// </summary>
		/// <exception cref="ArgumentNullException"><paramref name="site"/> is null</exception>
		/// <exception cref="ArgumentNullException"><paramref name="fileUrl"/> is null</exception>
		public static Stream ReadFileStream(this SPSite site, string fileUrl)
		{
			if (site == null) throw new ArgumentNullException("site");
			if (fileUrl == null) throw new ArgumentNullException("fileUrl");

			using (var dWeb = site.FindWebByUrl(fileUrl))
			{
				var web = dWeb.Disposable;

				return web.ReadFileStream(web.GetWebRelativeUrl(fileUrl));
			}
		}

		/// <summary>
		/// Save whole file
		/// </summary>
		/// <exception cref="ArgumentNullException"><paramref name="site"/> is null</exception>
		/// <exception cref="ArgumentNullException"><paramref name="fileUrl"/> is null</exception>
		public static bool SaveFile(this SPSite site, string fileUrl, byte[] data, bool overwrite)
		{
			if (site == null) throw new ArgumentNullException("site");
			if (fileUrl == null) throw new ArgumentNullException("fileUrl");

			using (var dWeb = site.FindWebByUrl(fileUrl))
			{
				var web = dWeb.Disposable;

				return web.SaveFile(web.GetWebRelativeUrl(fileUrl), data, overwrite);
			}
		}

		/// <summary>
		/// Save whole file
		/// </summary>
		/// <exception cref="ArgumentNullException"><paramref name="site"/> is null</exception>
		/// <exception cref="ArgumentNullException"><paramref name="fileUrl"/> is null</exception>
		public static bool SaveFile(this SPSite site, string fileUrl, Stream data, bool overwrite)
		{
			if (site == null) throw new ArgumentNullException("site");
			if (fileUrl == null) throw new ArgumentNullException("fileUrl");

			using (var dWeb = site.FindWebByUrl(fileUrl))
			{
				var web = dWeb.Disposable;

				return web.SaveFile(web.GetWebRelativeUrl(fileUrl), data, overwrite);
			}
		}
	}
}